﻿namespace Jsl.StyleCop.CSharp.MaintainabilityRules
{
    using Jsl.StyleCop.CSharp.UnitTesting;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class TestCheckForNullInDispose : TestBase<RuleAnalyzer>
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="TestCheckForNullInDispose"/> class.
        /// </summary>
        public TestCheckForNullInDispose()
            : base(RuleAnalyzer.CheckForNullInDispose)
        {
        }
        #endregion Constructors

        [TestMethod]
        public void Non_dispose_methods_are_ignored()
        {
            this.TestMethod(
                @"protected override void NotDispose(bool disposing)
                {
                    this.field.Dispose();
                    this.field = null;
                }")
                .NoMoreViolations();

            this.TestMethod(
                @"protected override void NotDispose(bool disposing)
                {
                    field.Dispose();
                    field = null;
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void No_problems_if_field_not_set_to_null()
        {
            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    this.field.Dispose();
                }")
                .NoMoreViolations();

            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    field.Dispose();
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void No_problems_if_field_is_checked_for_null()
        {
            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    if (this.field != null)
                    {
                        this.field.Dispose();
                        this.field = null;
                    }
                }")
                .NoMoreViolations();

            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    if (field != null)
                    {
                        field.Dispose();
                        field = null;
                    }
                }")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Violation_because_not_checking_for_null_on_method_call()
        {
            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    this.field.AnyMethod();
                    this.field = null;
                }")
                .AssertViolation(3, "Check for null before using the 'field' member. This would cause an exception if the Dispose was called more than once.")
                .NoMoreViolations();

            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    field.AnyMethod();
                    field = null;
                }")
                .AssertViolation(3, "Check for null before using the 'field' member. This would cause an exception if the Dispose was called more than once.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Violation_because_not_checking_for_null_on_member_reference()
        {
            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    this.field.Property = 5;
                    this.field = null;
                }")
                .AssertViolation(3, "Check for null before using the 'field' member. This would cause an exception if the Dispose was called more than once.")
                .NoMoreViolations();

            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    field.Property = 5;
                    field = null;
                }")
                .AssertViolation(3, "Check for null before using the 'field' member. This would cause an exception if the Dispose was called more than once.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void Violation_because_not_checking_correct_field()
        {
            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    if (this.otherField != null)
                    {
                        this.field.AnyMethod();
                        this.field = null;
                    }
                }")
                .AssertViolation(5, "Check for null before using the 'field' member. This would cause an exception if the Dispose was called more than once.")
                .NoMoreViolations();

            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    if (otherField != null)
                    {
                        field.AnyMethod();
                        field = null;
                    }
                }")
                .AssertViolation(5, "Check for null before using the 'field' member. This would cause an exception if the Dispose was called more than once.")
                .NoMoreViolations();
        }

        [TestMethod]
        public void No_problems_if_field_is_checked_for_null_multiple_levels_above()
        {
            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    if (this.field != null)
                    {
                        if (x == 5)
                        {
                            if (y == 6)
                            {
                                this.field.Dispose();
                                this.field = null;
                            }
                        }
                    }
                }")
                .NoMoreViolations();

            this.TestMethod(
                @"protected override void Dispose(bool disposing)
                {
                    if (field != null)
                        if (x == 5)
                            if (y == 6)
                            {
                                field.Dispose();
                                field = null;
                            }
                }")
                .NoMoreViolations();
        }
    }
}